package com.codefish.codefishseckill.sentinel;

import com.codefish.codefishseckill.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;

/**
 * API访问限流服务类
 *
 * @author codefish
 * @version 1.0
 * @date 2022/08/04 上午 10:32
 */
@Service
@Slf4j
public class CurrentLimitAccessServiceImpl implements CurrentLimitAccessService {
    @Autowired
    RedisTemplate<String, Object> redisTemplate;
    @Autowired
    LimitAccessProperty property;

    @Override
    public boolean canAccessURI(HttpServletRequest request, User user) {

        if (user == null) {
            log.info("用户信息为null，拒绝访问请求");
            return false;
        }

        Integer limitCount = property.getRules().get(request.getRequestURI());

        if (limitCount == null) {
            log.info("请求不在限流监控范围内，放行");
            return true;
        }

        String tokenRedisKey = TOKEN_REDIS_PREFIX + request.getRequestURI() + ":" + user.getUsername();
        String timestampRedisKey = TIMESTAMP_REDIS_PREFIX + request.getRequestURI() + ":" + user.getUsername();

        Double nowCount = (Double) redisTemplate.opsForValue().get(tokenRedisKey);
        Long timestamp = (Long) redisTemplate.opsForValue().get(timestampRedisKey);
        double rate = limitCount * 1.0 / 1000;

        if (nowCount == null) {
            nowCount = (double) limitCount;
        }
        long nowStamp = System.currentTimeMillis();
        if (timestamp == null) {
            timestamp = nowStamp;
        }
        nowCount = Math.min((double) limitCount, nowCount + (nowStamp - timestamp) * rate);

        boolean res;
        if (nowCount < 1) {
            log.info("限流！！！");
            res = false;
        } else {
            nowCount -= 1;
            res = true;
        }
        redisTemplate.opsForValue().set(tokenRedisKey, nowCount);
        redisTemplate.opsForValue().set(timestampRedisKey, nowStamp);
        return res;

    }
}
